package org.dy.db.model.crud;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.dy.db.Page;
import org.dy.db.model.CachedModel;
import org.dy.db.model.FieldInfo;
import org.dy.db.model.Row;
import org.dy.db.model.SQLGenerator;
import org.dy.db.support.BaseDAO;
import org.dy.db.support.DefaultDAOSupporter;

public class CrudModel extends CachedModel implements Crudable {

	private SQLGenerator generator;

	private BaseDAO dao;

	public CrudModel(String dir, 
			String app, String module, String name) {
		super(dir, app, module, name);
		init();
	}
	
	private void init() {
		loadFieldInfo();
		generator = new SQLGenerator(th, tl, null);

		int refreshPeriod = th.getCacherefreshPeriod();
		if(th.isCache() && refreshPeriod > 0)
			dao = DefaultDAOSupporter.getQueryCachedDAO(refreshPeriod);
		else
			dao = DefaultDAOSupporter.getBaseDAO();
	}
	
	/*
	 *  mod related
	 */
	private int getMod(){
		if(th != null)
			return th.getMod();
		return -1;
	}
	
	public boolean canRead(){
		return true;
	}
	
	public boolean canAddOrUpdate(){
		int mod = getMod();
		switch(mod){
		case 3:	return true;
		case 7:	return true;
		default: return false;
		}
	}
	
	public boolean canModify(){
		return canAddOrUpdate();
	}
	
	public boolean canDelete(){
		int mod = getMod();
		switch(mod){
		case 5:	return true;
		case 7:	return true;
		default: return false;
		}
	}

	
	@SuppressWarnings("unchecked")
	private List<Row> changeList(List<Map> list){
		List<Row> resList = new ArrayList<Row>();
		for(Map map : list){
			Row row = changeMapToRow(map);
			resList.add(row);
		}
		return resList;
	}
	
	@SuppressWarnings("unchecked")
	private Row changeMapToRow(Map map){
		if(map == null || map.isEmpty())
			return null;
		Row row = new Row();
		Set<String> keys = map.keySet();
		for(String key : keys){
			FieldInfo fi = th.getFieldByName(key);
			if(fi != null){
				fi.setValue(map.get(key));
				row.getFields().add(fi);
			}
		}
		Collections.sort(row.getFields());
		return row;
	}
	
	public boolean containField(String name){
		return th.getFieldByName(name) != null;
	}
	
	public Map<String, List<Row>> getRelationRowList(){
		List<FieldInfo> ffl = th.getFkInfoList();
		Map<String, List<Row>> map = new HashMap<String, List<Row>>();
		for(FieldInfo ffi:ffl){
			if(ffi != null){
				String querySql = generator.getQueryRelationSql(ffi);
				List<Map> rl = dao.queryForList(querySql, ffi.getValue() != null?new Object[] {ffi.getValue()}:null);
				map.put(ffi.getName(), changeList(rl));
			}
		}
		return map;
	}
	
	public List<Row> getRelationRowList(FieldInfo ffi){
		if(ffi != null && ffi.isFk() && ffi.getFkTableName() != null){
			String querySql = generator.getQueryRelationSql(ffi);
			List<Map> rl = dao.queryForList(querySql, ffi.getValue() != null?new Object[] {ffi.getValue()}:null);
			return changeList(rl);
		}
		return null;
	}
	
	public List<Row> getRelationRow(String fieldName){
		Map<String, List<Row>> map = getRelationRowList();
		return map.get(fieldName);
	}
	
	//*******************************************************
	public boolean create(Row row) throws CrudOptException {
		// TODO Auto-generated method stub
		return dao.update(generator.getInsertSql(row), generator
				.getRowValueList(row));
	}

	public boolean createBatch(Row[] rows) throws CrudOptException {
		if(rows.length > 0){
			Row row = rows[0];
			String sql = generator.getInsertSql(row);
			
			List<Object[]> args = new ArrayList<Object[]>();
			for(Row one:rows){
				args.add(generator.getRowValueList(one));
			}
			return dao.updateBatch(sql, args);
		}
		return false;
	}

	public boolean delete(FieldInfo fi) throws CrudOptException {
		String sql = generator.getDeleteSql(fi.getName());
		return dao.update(sql, new Object[] {fi.getValue()});
	}
	
	public boolean delete(Row row) throws CrudOptException {
		String sql = generator.getDeleteSql(row);
		return dao.update(sql, generator.getRowValueList(row));
	}
	
	public Row getOne(Object value) throws CrudOptException {
		FieldInfo fi = th.getPkInfo();
		fi.setValue(value);
		return getOne(fi);
	}

	public Row getOne(FieldInfo fi) throws CrudOptException {
		Row row = new Row();
		row.getFields().add(fi);
		return getOne(row );
	}
	
	public Row getOne(Row row) throws CrudOptException {
		String sql = generator.getQueryExtSql(row);
		Map map = dao.queryForMap(sql, generator.getRowValueList(row));
		return changeMapToRow(map);
	}

	public Row getNext(FieldInfo fi) {
		return null;
	}

	public List<Row> listAll() throws CrudOptException {
		// TODO Auto-generated method stub
		List<Map> rlist = dao.queryForList(generator.getQueryAllSql());
		return changeList(rlist);
	}
	
	public List<Row> listFields(String fields) throws CrudOptException {
		// TODO Auto-generated method stub
		List<Map> rlist = dao.queryForList(generator.getQuerySql(fields, null, null, null));
		return changeList(rlist);
	}
	
	public List<Row> listByQuery(Row row) throws CrudOptException {
		String sql = generator.getQueryExtSql(row);
		String[] obj = generator.getRowValueList(row);
		List<Map> rlist = dao.queryForList(sql, obj);
		return changeList(rlist);
	}

	public Page listInPage() throws CrudOptException {
		// TODO Auto-generated method stub
		return listInPage(1,1);
	}
	
	@SuppressWarnings("unchecked")
	public Page listPageByQuery(Row row, int cp, int npp) throws CrudOptException {
		// TODO Auto-generated method stub
		String sql = generator.getQueryExtSql(row);
		String[] obj = generator.getRowValueList(row);
		Page pi = dao.queryForPage(sql, obj, cp, npp);
		pi.setResultList(changeList(pi.getResultList()));
		return pi;
	}
	
	@SuppressWarnings("unchecked")
	public Page listInPage(int cp, int npp) throws CrudOptException {
		String sql = generator.getQueryAllSql();
		Page pi = dao.queryForPage(sql, cp, npp);
		pi.setResultList(changeList(pi.getResultList()));
		return pi;
	}

	public boolean update(Row row) throws CrudOptException {
		String sql = generator.getUpdateSql(row);
		return dao.update(sql, generator.getRowValueList(row, true));
	}

	public boolean updateBatch(Row[] rows) throws CrudOptException {
		if(rows.length > 0){
			Row row = rows[0];
			String sql = generator.getUpdateSql(row);
			
			List<Object[]> args = new ArrayList<Object[]>();
			for(Row one:rows){
				args.add(generator.getRowValueList(one));
			}
			return dao.updateBatch(sql, args);
		}
		return false;
	}

}
